/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package web.compliance.mail.browser;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.logging.Level;
import javax.mail.Address;
import javax.mail.BodyPart;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.NoSuchProviderException;
import javax.mail.Session;
import javax.mail.Store;
import org.apache.log4j.Logger;


/**
 * This class loads the mails from the designated source and adds them to the mailing index..
 * @author Aya
 */
public class MailFetcher {
    
    static Logger log = Logger.getLogger(MailFetcher.class.getName());
    static List<Mail> mail;
    static String last_user = "";
    Properties properties;
    SearchEngine lucene;

    /**
     * Constructor of the MailFetcher..
     * Initializes the properties and the search engine objects for further use.
     */
    public MailFetcher() {
        properties = new Properties();
        lucene = new SearchEngine();
    }
    
    /**
     * This function converts an array of messages to a List of Mail objects
     * @param messages The array of messages to be parsed
     * @return List of Mail objects
     */
    public void parseMessages (Message[] messages)
    {
        //List<Mail> mail = new ArrayList<Mail>();
        for(Message msg: messages){
            Mail m = new Mail();
            try{
                m.setDate(msg.getSentDate());
                m.setSubject(msg.getSubject());
                Address[] in = msg.getFrom();
                for (Address address : in) {
                    m.setFrom(m.getFrom()+address.toString());
                }
                Multipart mp = (Multipart) msg.getContent();
                BodyPart bp = mp.getBodyPart(0);
                m.setContent(bp.getContent().toString());
                mail.add(m);
                lucene.addMail(m);
            }
            catch(MessagingException e){
                log.warn("Error parsing a Gmail Message Header");
                //return null;
            }
            catch(IOException e){
                log.warn("Error extracting a Gmail Message Content or writing Gmail Message to Lucene");
                //return null;
            }
        }
        //return mail;
    }
    
    /**
     * Checks whether an email,password tuple is valid or not
     * @param e The email to be checked
     * @param p The password to be checked
     * @return A string true if valid and false if invalid
     */
    public String valid(String e, String p)
    {
        properties.setProperty("mail.store.protocol", "imaps");
        try {
            Session session = Session.getInstance(properties, null);
            Store store = session.getStore();
            store.connect("imap.gmail.com", e, p);
            store.getFolder("INBOX");
        } catch(Exception ex){
            return "false";
        }
        return "true";
    }
    
    /**
     * This function fetches all mail messages in a given Gmail address
     * @param e The Gmail address
     * @param p The password
     * @return List of Mail objects
     */
    public List<Mail> getGmail(String e, String p)
    {
        mail = new ArrayList<Mail>();
        properties.setProperty("mail.store.protocol", "imaps");
        try {
            Session session = Session.getInstance(properties, null);
            Store store = session.getStore();
            store.connect("imap.gmail.com", e, p);
            Folder inbox = store.getFolder("INBOX");
            inbox.open(Folder.READ_ONLY);
            Message[] messages = inbox.getMessages();
            lucene.clearIndex();
            this.parseMessages(messages);
            return mail;
        } catch (NoSuchProviderException ex) {
           log.warn("Error getting the session store");
           return null;
        } catch (MessagingException ex){
            log.warn("Error connecting to the store and reading messages");
            return null;
        } catch (IOException ex) {
            log.warn("Error clearing index");
            return null;
        }
    }
    
    /**
     * This function is the alternative to getGmail, it fetches the mails from the sample MBox file
     * @param mboxFile Input mBox file
     * @return A List of Mail objects
     */
    public List<Mail> getMBox(){
               
        InputStream mboxFile = this.getClass().getClassLoader().getResourceAsStream("/mboxfile.mbox");
        log.info("Loaded mbox file from disk.");
        
        /*
            System.out.println(mboxFile);
            char c;
            int i;
            try {
                while((i=mboxFile.read())!=-1)
                {
                    // converts integer to character
                    c=(char)i;
                    
                    // prints character
                    System.out.print(c);
                }
            } catch (IOException ex) {
                java.util.logging.Logger.getLogger(MailFetcher.class.getName()).log(Level.SEVERE, null, ex);
            }
            */
        try {
            lucene.clearIndex();    
        } catch (IOException ex) {
            log.warn("Error clearing index");
        }
        
        java.io.BufferedReader mboxBr;
        mboxBr = new java.io.BufferedReader(new InputStreamReader(mboxFile));
        
        String line;
        int endOfWord;
        String from = "";
        String adressat = "";
        String fromAddress;
        String date;
        String subject = "";
        StringBuilder content = new StringBuilder();
        boolean contentmarker = false;
        String in_reply_to = "";
        String msg_id = "";
        boolean attachment = false;
        int lineNum=0;
        int endOfWord_at;
        int startLineNum;
        int endLineNum;
        Date formateddate;
        mail = new ArrayList<Mail>();

        try{
        line = mboxBr.readLine();
        while (line!=null)
        {
            lineNum++;
            if (line.startsWith("From "))
            {
                endOfWord_at = line.indexOf(" at ",5) + 4;
                endOfWord = line.indexOf(" ",endOfWord_at);
                fromAddress = line.substring(5,endOfWord);
                date = line.substring(endOfWord+2, line.length());
                
                while ((line=mboxBr.readLine())!=null && !(line.startsWith("From ")))
                {
                    lineNum++;
                    if(contentmarker) content.append(line + "\n");
                    if (line.startsWith("Subject: ")) subject = line.substring(9);
                    CharSequence cs = "(";
                    CharSequence cs1 = ")";
                    if (line.startsWith("From: ") && line.contains(cs) && line.contains(cs1)){
                        from = line.substring(6);
                        adressat = from.substring(from.indexOf("(")+1,from.lastIndexOf(")"));
                    }
                    if (line.startsWith("In-Reply-To: <")) in_reply_to = line.substring(14,line.length()-1);
                    if (line.startsWith("Message-ID: <")){
                        msg_id = line.substring(14,line.length()-1);
                        startLineNum = lineNum+1;
                        contentmarker = true;
                    }
                    if (line.startsWith("Content-Type: ")) if (line.substring(14).startsWith("Multipart")) attachment = true;
                    
                }
                endLineNum=lineNum;
                
                Mail m = new Mail();
                /*
                System.out.println("Read from mboxFile:");
                System.out.println("From: "+ from);
                System.out.println("Name: "+ adressat);
                System.out.println("Message-ID: "+ msg_id);
                System.out.println("Reply to: "+ in_reply_to);
                System.out.println("Subject: " + subject);
                System.out.println("Content: " + content.toString());
                System.out.println("attachment: " + attachment);
                System.out.println("date: " + date);
                */
                
                m.setFrom(from);
                m.setName(adressat);
                m.setMsgId(msg_id);
                m.setReplyTo(in_reply_to);
                m.setSubject(subject);
                m.setContent(content.toString());
                content.delete(0,content.length());
                contentmarker = false;
                m.setAttachement(attachment);
                formateddate = new SimpleDateFormat("EEE MMM dd kk:mm:ss yyyy", Locale.ENGLISH).parse(date);
                                
                m.setDate(formateddate);
                mail.add(m);
                lucene.addMail(m);
                
                /*
                System.out.println("formated Date: " + formateddate);
                System.out.println("line: " + lineNum);
                System.out.println("startline: " + startLineNum);
                System.out.println("endline: " + endLineNum);
                System.out.println("--------------------------");
                */
            }
        }
        
        
        mboxBr.close();
        } catch (IOException ex) {
            log.warn("Error reading mboxFile. Miss-read content.");
        } catch (ParseException ex) {
                    log.warn("Error while parsing date.");
        }
        //System.out.println("Read " + mail.size() + " mails from mbox file.");
        
        log.warn("Read " + mail.size() + " mails from mbox file.");
        
        return mail;
    }
    
    /**
     * Searches for mails with a given keyword in the Subject field
     * @param keyword The keyword to search for
     * @return The list of filtered mails
     */
    public List<Mail> getMailsWithSubject(String keyword){
        return lucene.getMails("subject", keyword, 10);
    }
    
    /**
     * Searches for mails with a given keyword in the Content field
     * @param keyword The keyword to search for
     * @return The list of filtered mails
     */
    public List<Mail> getMailsWithContent(String keyword){
        return lucene.getMails("content", keyword, 10);
    }
    
    /**
     * Searches for mails with a given keyword in the Sender field
     * @param keyword The keyword to search for
     * @return The list of filtered mails
     */
    public List<Mail> getMailsWithSender(String keyword){
        return lucene.getMails("sender", keyword, 10);
    }
    
    /**
     * Searches for mails with a given date range
     * @param start_date The start of the date interval
     * @param end_date The end of the date interval
     * @return The list of filtered mails
     */
    public List<Mail> getMailsBetween(String start_date, String end_date){
        return lucene.getMailsInRange(getDate(start_date), getDate(end_date), 10);
    }
    
    /**
     * Filters mails to only those received before a given date
     * @param date The date to use for filtering
     * @return The list of filtered mails
     */
    public List<Mail> getMailsBefore(String date){
        return lucene.getMailsInRange(null, getDate(date), 10);
    }
    
    /**
     * Filters mails to only those received after a given date
     * @param date The date to use for filtering
     * @return The list of filtered mails
     */
    public List<Mail> getMailsAfter(String date){
        return lucene.getMailsInRange(getDate(date), null, 10);
    }

    /**
     * Filters mails using a complex query
     * @param sender search for keyword in sender field
     * @param content search for keyword in content field
     * @param subject search for keyword in subject field
     * @param keyword The keyword to search for
     * @param sdate The start of the date range (or null for open)
     * @param edate The end of the date range (or null for open)
     * @return The list of filtered mails
     */
    List<Mail> multifilter(boolean sender, boolean content, boolean subject, 
            String keyword, String sdate, String edate) {
        
        List<Mail> m = new ArrayList<Mail>();
        if(sender)
            m = union(m, lucene.getMailsWithFilter("sender", keyword, getDate(sdate), getDate(edate), 10));
        if(content)
            m = union(m, lucene.getMailsWithFilter("content", keyword, getDate(sdate), getDate(edate), 10));
        if(subject)
            m = union(m, lucene.getMailsWithFilter("subject", keyword, getDate(sdate), getDate(edate), 10));
        return m;
    }
    
    /**
     * Filters mails using a complex query of filtering each field by a separate keyword
     * @param sender keyword to search in sender field
     * @param content keyword to search in content field
     * @param subject keyword to search in subject field
     * @param sdate The start of the date range (or null for open)
     * @param edate The end of the date range (or null for open)
     * @return The list of filtered mails
     */
    List<Mail> multiKeywordFilter(String sender, String content, String subject, String sdate, String edate) {
        
        List<Mail> m = new ArrayList<Mail>();
        if(sender != null)
            m = union(m, lucene.getMailsWithFilter("sender", sender, getDate(sdate), getDate(edate), 10));
        if(content != null)
            m = union(m, lucene.getMailsWithFilter("content", content, getDate(sdate), getDate(edate), 10));
        if(subject != null)
            m = union(m, lucene.getMailsWithFilter("subject", subject, getDate(sdate), getDate(edate), 10));
        return m;
    }
    
    /**
     * Converts the convention string to a date
     * @param s The string to convert
     * @return The converted date
     */
    public Date getDate(String s){
        if(s != null){
            String[] d = s.split("-");
            return new Date(Integer.parseInt(d[2])-1900, Integer.parseInt(d[1])-1, Integer.parseInt(d[0]));
        } else
            return null;
    }
    
    /**
     * Performs the union of 2 lists of mails
     * @param orig The list to add to
     * @param toadd The list to be added
     * @return The union of both lists
     */
    private List<Mail> union(List<Mail> orig, List<Mail> toadd){
        for(int i=0; i<toadd.size(); i++){
            String content = toadd.get(i).getContent();
            boolean found = false;
            for(int j=0; j<orig.size(); j++){
                if(orig.get(j).getContent().equals(content))
                    found = true;
            }
            if(!found)
                orig.add(toadd.get(i));
        }
        return orig;
    }
}
